package com.yangchao.webflux.webserver.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import reactor.core.publisher.Mono;

@Configuration
public class CustomHttpServiceProxyFactoryConfig {

    private static final Logger logger = LoggerFactory.getLogger(CustomHttpServiceProxyFactoryConfig.class);

    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder()
                .filter(logRequest())
                .filter(logResponse())
                .filter(handleErrors());
    }

    @Bean
    public HttpServiceProxyFactory httpServiceProxyFactory(WebClient.Builder webClientBuilder) {
        WebClient webClient = webClientBuilder.baseUrl("http://localhost:8080").build();
        return HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build();
    }

    private ExchangeFilterFunction logRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
            StringBuilder curlCommand = new StringBuilder("curl -X ")
                    .append(clientRequest.method())
                    .append(" '")
                    .append(clientRequest.url())
                    .append("'");

            clientRequest.headers().forEach((name, values) -> values.forEach(value ->
                    curlCommand.append(" -H '").append(name).append(": ").append(value).append("'")
            ));
            logger.info("Request \n: {}", curlCommand);
            return Mono.just(clientRequest);
        });
    }

    private ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            logger.info("Response code: {}", clientResponse.statusCode());
            clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value ->
                    logger.info("header {}: {}", name, value)
            ));
            return Mono.just(clientResponse);
        });
    }


    private ExchangeFilterFunction handleErrors() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            if (clientResponse.statusCode().isError()) {
                return clientResponse.bodyToMono(String.class)
                        .flatMap(body -> Mono.error(new RuntimeException("Error response: " + body)));
            }
            return Mono.just(clientResponse);
        });
    }
}